home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_56 / dmastep5.asm < prev    next >
Assembly Source File  |  1995-01-01  |  17KB  |  426 lines

  1. ;══════════════════════════════════════════════════════════════════════════════
  2. ; Play 8bit DMA mode for SoundBlaster v2.00
  3. ;   André Baresel (with some help from Craig Jackson)
  4. ;──────────────────────────────────────────────────────────────────────────────
  5. ; STATUS: DOES WORK ON SB16/SBPRO2/SB2.0
  6. ;──────────────────────────────────────────────────────────────────────────────
  7. ; Requirements: 80286, SoundBlaster (see BASEADDR,DMA channel,IRQ number)
  8. ; Resolutions : 8-bit / 4..44khz (highspeed DMA)
  9. ; Parameters  : none
  10. ; Notes:
  11. ;     ■ pause/continue fails on SB2.0,SB1.5,SB1.0 (and all below :)
  12. ;     ■ To creat a 8 bit mono unsigned file do :   "VOC2RAW TEST1.VOC /I"
  13. ;
  14. ; ■ DSP command 48h  ... set DMAbuffer size
  15. ; ■ DSP command 90h  ... highspeed 8bit autoinit
  16. ; ■ DSP command 40h  ... set sample rate
  17. ; ■ DSP command D1h  ... Enable Speaker
  18. ; ■ DSP command D3h  ... Disable Speaker
  19. ; ■ DSP command D0h  ... Halt 8 bit DMA transfer
  20. ; ■ DSP command D4h  ... continue 8 bit DMA transfer
  21. ;
  22.  
  23. .MODEL small
  24. .286
  25.  
  26. ; CONSTANTS ───────────────────────────────────────────────────────────────────
  27.  
  28. ; SoundBlaster SETUP
  29. BASEADDR           EQU 0220h       ;SoundBlaster base address
  30. IRQ7               EQU 15          ;SoundBlaster IRQ
  31. DMAchannel         EQU 1           ;SoundBlaster DMA channel
  32.  
  33. ; PIC MASKS FOR MASK/DEMASK IRQ
  34. PICANDMASK         EQU 01111111b   ;'AND' PIC mask for clear IRQ7
  35. PICORMASK          EQU 10000000b   ;'OR' PIC mask for set IRQ7
  36.  
  37. ; DMA CONTROLLER REGISTERS :
  38. WRITEMASK          EQU 00ah         ;WRITE MASK REGISTER
  39. WRITEMODE          EQU 00bh         ;WRITE MODE REGISTER
  40. CLEARFLIPFLOP      EQU 00ch
  41. PAGE_CHN           EQU 083h         ;PAGE REGISTER FOR DMAchannel 1
  42. BASE_CHN           EQU 002h         ;BASEADDRESS REGISTER DMA 1
  43. COUNT_CHN          EQU 003h         ;COUNT REGISTER DMAchannel 1
  44.  
  45. ; SAMPLERATE : (if you change it pay attention to maximum samplerate)
  46. TIMECONST          EQU 165          ; = 10989 Hz (256-1000000/11000)
  47.  
  48. ; DMA WRITE MODE
  49. WANTEDMODE         EQU 01001000b    ; singlemode, autoinit, readmode
  50.  
  51. ;──────────────────────────────────────────────────────────────────────────────
  52. ; MACRO DEFINITIONs
  53. ;──────────────────────────────────────────────────────────────────────────────
  54. STARTUP                 MACRO
  55. ; MASM 5.x COMPATIBILITY
  56. __start:                mov     ax,DGROUP
  57.                         mov     ds,ax
  58.                         mov     bx,ss
  59.                         sub     bx,ax
  60.                         shl     bx,004h
  61.                         mov     ss,ax
  62.                         add     sp,bx
  63. ENDM
  64.  
  65. WAITWRITE               MACRO
  66. LOCAL                   loopWait,endloop
  67. ;          Arguments : DX = Status port (BASEADDR+0Ch)
  68. ;          Returns   : n/a
  69. ;          Destroys  : AL
  70.  
  71.                         push    cx
  72.                         xor     cx,cx           ; need that for slow SBs !
  73. loopWait:               dec     cx
  74.                         jz      endloop
  75.                         in      al,dx           ; AL = WRITE COMMAND STATUS
  76.                         or      al,al
  77.                         js      loopWait        ; Jump if bit7=1 - writing not allowed
  78. endloop:                pop     cx
  79. ENDM
  80.  
  81. WAITREAD                MACRO
  82. LOCAL                   loopWait,endloop
  83. ;          Arguments : DX = Status port   (normaly BASEADDR+0Eh)
  84. ;          Returns   : n/a
  85. ;          Destroys  : AL
  86.  
  87.                         push    cx
  88.                         xor     cx,cx           ; need that for slow SBs !
  89. loopWait:               dec     cx
  90.                         jz      endloop
  91.                         in      al,dx           ; AL = DATA AVAILABLE STATUS
  92.                         or      al,al
  93.                         jns     loopWait        ; Jump if bit7=0 - no data available
  94. endloop:                pop     cx
  95. ENDM
  96.  
  97. RESET_DSP               MACRO
  98. local                   SBthere
  99. ;          Arguments : n/a
  100. ;          Returns   : n/a
  101. ;          Destroys  : DX,AL
  102.  
  103.                         mov      dx,BASEADDR+06h
  104.                         mov      al,1
  105.                         out      dx,al          ; start DSP reset
  106.  
  107.                         in       al,dx
  108.                         in       al,dx
  109.                         in       al,dx
  110.                         in       al,dx          ; wait 3 µsec
  111.  
  112.                         xor      al,al
  113.                         out      dx,al          ; end DSP Reset
  114.  
  115.                         add      dx,08h         ; dx = DSP DATA AVAILABLE
  116.                         WAITREAD
  117.                         sub      dx,4           ; dx = DSP Read Data
  118.                         in       al,dx
  119.                         cmp      al,0aah        ; if there is a SB then it returns 0AAh
  120.                         je       SBthere
  121.                         jmp      RESET_ERROR    ; No SB - exit program
  122. SBthere:
  123. ENDM
  124. ;─── End of Macrodefinitions ──────────────────────────────────────────────────
  125.  
  126. .STACK 100h
  127.  
  128. .DATA
  129. ;──────────────────────────────────────────────────────────────────────────────
  130. ; TWO COPIES FOR PAGE OVERRIDE REASONS :
  131.  
  132. SAMPLEBUFFER LABEL BYTE
  133.     INCLUDE TEST1.INC         ; FIRST COPY OF SAMPLE SOUND
  134. SAMPLEBUFFEREND LABEL BYTE
  135.     INCLUDE TEST1.INC         ; SECOND COPY OF SAMPLE SOUND
  136.  
  137.     ready               db 0
  138.  
  139.     information         db 13,10,'DMASTEP5.EXE - play/pause/restart a sound now in highspeed'
  140.                         db 13,10,'mode (up to 44kHz samplerate)'
  141.                         db 13,10,' Keys :'
  142.                         db 13,10,' "R" ........... restart sample'
  143.                         db 13,10,' "P" ........... pause playing and continue then with any key (SB16+)'
  144.                         db 13,10,' any other ..... quit program','$'
  145.     sberror             db 13,10,'No SoundBlaster at this BASEADDR ! PROGRAM HALTED.','$'
  146.  
  147.     OLDInterruptSEG     dw ?
  148.     OLDInterruptOFS     dw ?
  149.  
  150.     SAMPLEBUFFERLENGTH = offset SAMPLEBUFFEREND - offset SAMPLEBUFFER
  151. ;──────────────────────────────────────────────────────────────────────────────
  152. .CODE
  153.  STARTUP
  154.  
  155.            RESET_DSP
  156.  
  157.            ; WRITE INFORMATION TO SCREEN :
  158.            mov     dx,offset information
  159.            mov     ah,9
  160.            int     21h                         ; write program information to screen
  161.  
  162.            ; ENABLE SB SPEAKERS (for all SBs <SB16)
  163.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  164.            WAITWRITE
  165.            mov     al,0D1h                     ; AL = Enable speaker
  166.            out     dx,al                       ; Output: DSP Write Data or Command
  167.  
  168.            ; SETUP IRQ :
  169.            xor     ax,ax
  170.            mov     es,ax                       ; es to page 0 (Interrupt table)
  171.            mov     si,IRQ7*4                   ; si = position in interrupt table
  172.  
  173.            ; DISABLE IRQ
  174.            in      al,021h
  175.            and     al,PICANDMASK               ; SET MASK REGISTER BIT TO DISABLE INTERRUPT
  176.            out     021h,al
  177.  
  178.            ; CHANGE POINTER IN INTERRUPT TABLE
  179.            mov     ax,es:[si]
  180.            mov     [OLDInterruptOFS],ax        ; save offset of old interupt vector for restoring
  181.            mov     ax,OFFSET OWN_IRQ
  182.            mov     es:[si],ax                  ; set offset of new interrupt routine
  183.            mov     ax,es:[si+2]
  184.            mov     [OLDInterruptSEG],ax        ; save segment of old interupt vector for restoring
  185.            mov     ax,cs
  186.            mov     es:[si+2],ax                ; set segment of new interrupt routine
  187.  
  188.            ; CHANGE PIC MASK :
  189.            in      al,021h
  190.            and     al,PICANDMASK   ; CLEAR MASK REGISTER BIT TO ENABLE INTERRUPT
  191.            out     021h,al
  192.  
  193. ;──────────────────────────────────────────────────────────────────────────────
  194. ; MAIN LOOP - here we restart the sample later
  195.  
  196. start:
  197.                 mov     si,offset samplebuffer
  198.                 mov     cx,SAMPLEBUFFERLENGTH-1
  199.  
  200.                 mov     [ready],0
  201.  
  202. ;──────────────────────────────────────────────────────────────────────────────
  203. ; calculate page and offset for DMAcontroller :
  204. ;
  205. ; segment*16+offset - 20bit memory location -> upper 4 bits  = page
  206. ;                                              lower 16 bits = offset
  207. ;──────────────────────────────────────────────────────────────────────────────
  208.            mov     si,offset samplebuffer
  209.            mov     cx,SAMPLEBUFFERLENGTH-1
  210.  
  211.            mov     ax,ds
  212.            rol     ax,4                ; * 16 - higher 4 bits in al
  213.            mov     bl,al
  214.            and     bl,00fh             ; BL - higher 4 bits
  215.            and     al,0f0h             ; clear higher 4bits in AL
  216.            add     si,ax               ; SI = offset
  217.            adc     bl,0                ; BL = page
  218. ;──────────────────────────────────────────────────────────────────────────────
  219. ; check for DMApage override :
  220. ; ... problem: DMA controller separates memory into 64KB pages, you can only
  221. ; transfer data is placed in one page - no page overrides are allowed
  222. ;──────────────────────────────────────────────────────────────────────────────
  223. ; To solve that :
  224. ; creat a DMA buffer with double size you want - if the first part is placed
  225. ; on a page border the second part is for sure not
  226. ;──────────────────────────────────────────────────────────────────────────────
  227.            neg     si          ; si = 65536 - si   (bytes left to DMA page border)
  228.            cmp     si,cx       ; if si (bytes left to border) > cx (bytes to play)
  229.            ja      nooverride  ; then there's no page override
  230.  
  231.            ; WE HAVE TO USE SECOND PART
  232.            neg     si          ; si = offset of first part
  233.            add     si,cx       ; si = si + length of one part
  234.            inc     si          ; si=si+1 - start of second part
  235.            inc     bl          ; second part is then on the next page
  236.            neg     si          ; look at the next command ;)
  237.                                ; (that is better than a jump ?)
  238. nooverride:
  239.            neg     si
  240.  
  241. ;──────────────────────────────────────────────────────────────────────────────
  242. ; Setup DMA-controller :
  243. ;
  244. ; 1st  MASK DMA CHANNEL
  245. ;
  246.            mov     al,DMAchannel
  247.            add     al,4
  248.            out     WRITEMASK,al
  249. ;──────────────────────────────────────────────────────────────────────────────
  250. ; 2nd  CLEAR FLIPFLOP
  251. ;
  252.            out     CLEARFLIPFLOP,al
  253. ;──────────────────────────────────────────────────────────────────────────────
  254. ; 3rd  WRITE TRANSFER MODE
  255. ;
  256.            mov     al,WANTEDMODE
  257.            add     al,DMAchannel
  258.            out     WRITEMODE,al
  259. ;──────────────────────────────────────────────────────────────────────────────
  260. ; 4th  WRITE PAGE NUMBER
  261. ;
  262.            mov     al,bl
  263.            out     PAGE_CHN,al
  264. ;──────────────────────────────────────────────────────────────────────────────
  265. ; 5th  WRITE BASEADDRESS
  266. ;
  267.            mov     ax,si
  268.            out     BASE_CHN,al
  269.            mov     al,ah
  270.            out     BASE_CHN,al
  271. ;──────────────────────────────────────────────────────────────────────────────
  272. ; 6th  WRITE SAMPLELENGTH-1
  273. ;
  274.            mov     al,cl
  275.            out     COUNT_CHN,al
  276.            mov     al,ch
  277.            out     COUNT_CHN,al
  278. ;──────────────────────────────────────────────────────────────────────────────
  279. ; 7th  DEMASK CHANNEL
  280. ;
  281.            mov     al,DMAchannel
  282.            out     WRITEMASK,al
  283.  
  284. ;──────────────────────────────────────────────────────────────────────────────
  285. ; Setup SoundBlaster :
  286. ;
  287. ; 1st  SET TIMECONSTANTE
  288. ;
  289.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  290.            WAITWRITE
  291.            mov     al,040h                     ;AL = Set timeconstant
  292.            out     dx,al
  293.            WAITWRITE
  294.            mov     al,TIMECONST
  295.            out     dx,al
  296.  
  297. ;──────────────────────────────────────────────────────────────────────────────
  298. ; 2nd  use 8bit highspeed autoinit mode (DSPC 90h)
  299. ;
  300.            ;SETUP SIZE
  301.            WAITWRITE
  302.            mov     al,048h                     ;AL = set DMAbuffer size
  303.            out     dx,al
  304.            mov     cx,SAMPLEBUFFERLENGTH-1
  305.            WAITWRITE
  306.            mov     al,cl                       ;AL = LOWER PART SAMPLELENGTH
  307.            out     dx,al
  308.            WAITWRITE
  309.            mov     al,ch                       ;AL = HIGHER PART SAMPLELENGTH
  310.            out     dx,al
  311.  
  312.            ; SETUP PLAYMODE
  313.            WAITWRITE
  314.            mov     al,090h                     ;AL = highspeed DMA 8bit autoinit
  315.            out     dx,al
  316.  
  317. ; NOW TRANSFER ..... :)
  318.  
  319. waitloop:  mov     ah,01                       ;AH = Check for character function
  320.            int     016h                        ;   Interrupt: Keyboard
  321.            jz      waitloop                    ; wait for any key
  322.  
  323. readkey:   xor     ah,ah                       ;Read character, flush keypress
  324.            int     016h                        ;   Interrupt: Keyboard
  325.            cmp     al,'r'
  326.            je      restart
  327.            cmp     al,'p'
  328.            je      noexit
  329.            jmp     exit         ; <- sorry for this, but you know about 286 jmps
  330.                                 ; <- that's a reason why I use .386  :)
  331. noexit:    cmp     [ready],1                   ; sample still playing ?
  332.            je      waitloop                    ; yo ...
  333.            jmp     pause
  334. restart:   ; HERE RESTART SAMPLE :
  335.  
  336.            ; FIRST HALT SAMPLE IS CURRENTLY PLAYING
  337.            mov     dx,BASEADDR+0Ch
  338.            WAITWRITE
  339.            mov     al,0d0h                     ; AL = DSP halt 8bit DMA
  340.            out     dx,al
  341.            WAITWRITE
  342.            mov     al,0DAh                     ; AL = exit 8bit DMA
  343.            out     dx,al
  344.            WAITWRITE
  345.            mov     al,0D0h                     ; AL = DSP halt 8bit DMA
  346.            out     dx,al
  347.  
  348.            ; ON <SB16 YOU CAN'T HALT HIGHSPEED DMA YOU HAVE TO RESET !
  349.            RESET_DSP
  350.  
  351.            ; ENABLE SPEAKERS AFTER DSP RESET (that'll make that 'click' while restart)
  352.            mov     dx,BASEADDR+00Ch            ;DX = DSP Write Data or Command
  353.            WAITWRITE
  354.            mov     al,0D1h                     ;AL = Enable speaker
  355.            out     dx,al                       ;   Output: DSP Write Data or Command
  356.  
  357.            jmp        start
  358.  
  359. pause:     ; NOW PAUSE PLAYING : (only on SB16 models - we are in a highspeed mode)
  360.            mov     dx,BASEADDR+0Ch
  361.            WAITWRITE
  362.            mov     al,0d0h                     ; AL = DSP halt 8bit DMA
  363.            out     dx,al
  364.  
  365.            ; WAIT FOR ANY KEY :  (what is the <ANY> key ? =:| )
  366.            xor     ah,ah                       ;Read character, flush keypress
  367.            int     016h                        ;   Interrupt: Keyboard
  368.  
  369.            ; NOW CONTINUE
  370.            mov     dx,BASEADDR+0Ch
  371.            WAITWRITE
  372.            mov     al,0D4h                     ; AL = DSP continue 8bit DMA
  373.            out     dx,al
  374.            jmp     waitloop
  375.  
  376. exit:      ; FIRST HALT DMA TRANSFER (reset is the easiest way) :
  377.            RESET_DSP
  378.  
  379.            ; RESTORE PIC MASK
  380.            in      al,021h
  381.            or      al,PICORMASK                ;<-- SET REGISTER MASK BITS TO DISABLE
  382.            out     021h,al
  383.  
  384.            ; RESTORE IRQ :
  385.            xor     ax,ax
  386.            mov     es,ax                       ; es to page 0 (Interrupt table)
  387.            mov     si,IRQ7*4
  388.            mov     ax,[OLDInterruptOFS]
  389.            mov     es:[si],ax                  ; set old interrupt routine
  390.            mov     ax,[OLDInterruptSEG]
  391.            mov     es:[si+2],ax
  392.  
  393.            ; TERMINATE EXE:
  394. return2dos:
  395.            mov     ax,04c00h
  396.            int     21h
  397.  
  398. ; display information if Soundblaster is not on this baseaddress
  399. RESET_ERROR:
  400.            mov     dx,offset sberror
  401.            mov     ah,9
  402.            int     21h                         ; text output
  403.            jmp     return2dos
  404.  
  405. ;──────────────────────────────────────────────────────────────────────────────
  406. ; Our own IRQ for detecting end of playing
  407. ; It's generated by the SoundBlaster hardware
  408. ;──────────────────────────────────────────────────────────────────────────────
  409. OWN_IRQ:
  410.            push    ax
  411.            push    dx
  412.            push    ds
  413.            mov     dx,BASEADDR+00Eh            ;DX = DSP DATA AVAILABLE (IRQ ACKNOWLEDGE)
  414.            in      al,dx
  415.            mov     ax,@DATA
  416.            mov     ds,ax
  417.            mov     [READY],1                   ; Sample done ...
  418.            mov     al,020h
  419.            out     020h,al                     ;ACKNOWLEDGE HARDWARE INTERRUPT - PIC1
  420.            pop     ds
  421.            pop     dx
  422.            pop     ax
  423.            IRET
  424.  
  425. END     __start
  426.